package org.molamil.acto.types {
	import org.molamil.acto.core.parsers.PrioritizableParser;	
	
	import flash.utils.getDefinitionByName;	
	
	import org.molamil.acto.core.parsers.ParserError;	
	
	import flash.utils.getQualifiedClassName;	
	import flash.errors.IllegalOperationError;	
	
	/**
	 * Abstract implementation of a type parser used to convert a <code>String</code>
	 * into a different type, such as a <code>Boolean</code> or a <code>Date</code>
	 * 
	 * <p>This class is not meant to be instantiated but extended to
	 * to provide implementations for the specific types.</p>
	 * 
	 * <p>In order to create a custom implementation for an type parser you should
	 * do the following:</p>
	 * <ul>
	 * 	<li>Extend <code>AbstractTypeParser</code></li>
	 * 	<li>Declare and initalize <code>public static const PRIORITY:uint</code> 
	 * 		- otherwise the <code>AbstractTypeParser.PRIORITY</code> will be used.<br />
	 * 		The priority affects the order in which the type parser will be run in a
	 * 		<code>TypeResolver</code>, the higher the priority, the earlier will be run. 
	 * 		Note that TypeResolver will chose the type by matching the first type parser
	 * 		that executes a successful call to <code>parse()</code>, this is where the
	 * 		priority becomes important.</li>
	 * 	<li>Declare and initalize <code>public static const TYPE:Class</code> 
	 * 		- otherwise the <code>AbstractTypeParser.TYPE</code> will be used.<br />
	 * 		<code>TYPE</code> should represent the type of the return value when calling
	 * 		<code>parse()</code></li>
	 * 	<li>Override <code>doParse()</code> with your custom implementation.<br />
	 * 		Avoid overriding <code>parse()</code> as this wraps <code>doParse()</code> and
	 * 		checks for the type.</li>
	 * </ul>
	 * 
	 * @example
	 * <listing version="3.0" >
	 * public static const PRIORITY:uint = 500;
	 * public static const TYPE:Class = Number;
	 * 
	 * override protected function doParse(value:String):* {
	 * 	var n:Number = Number(value);
	 * 	if (!isNaN(n)) {
	 * 		return n;
	 * 	} else {
	 * 		throw new ParserError();
	 * 	}
	 * }
	 * </listing>
	 * 
	 * @author Jorge Hernandez
	 * @see org.molamil.acto.types.TypeResolver
	 */
	public class AbstractTypeParser implements PrioritizableParser {
		
		
		// CONSTANTS
		
		/**
		 * Represents the default <code>priority</code> for this class. This value is set
		 * to the <code>priority</code> property at construction time.
		 */
		public static const PRIORITY:uint = 0;
		
		/**
		 * Represents this type parser's type, in which the value will be converted
		 * when <code>parse()</code>
		 */
		public static const TYPE:Class = String;
		
		
		// PROPERTIES
		
		protected var _priority:uint;
		protected var type:Class;
		
		
		// CONSTRUCTOR
		
		/**
		 * Sets <code>priority</code> to this class' PRIORITY, initializes the type. 
		 */
		public function AbstractTypeParser() {
			
			var className:String = getQualifiedClassName(this);
			
			// Throw an error if instanciating this class, and not subclasses.
			if(className == "org.molamil.acto.types::AbstractTypeParser")
            	throw new IllegalOperationError("AbstractTypeParser must be extended");
            
            // Check the subclasses' constants to initialize priority and type.
			_priority = getDefinitionByName(className)["PRIORITY"];
			type = getDefinitionByName(className)["TYPE"];
				
			if (type == null)
				type = TYPE;
			
		}
		
		
		// PUBLIC METHODS
		
		/**
		 * Tests the value passed against the implicit class' type and 
		 * returns the typed value.
		 * 
		 * <p>When extending, it is recommended to override <code>doParse()</code> instead 
		 * and keep this implementation of <code>parse()</code>, as it in fact decorates 
		 * <code>doParse()</code> with testing for the class' <code>TYPE</code></p>
		 * 
		 * @param value <code>String</code> value to test
		 * @return The parsed value converted to the class' <code>TYPE</code>
		 * @throws org.molamil.acto.core.errors.ParserError The value can't
		 * 	be converted to the class' <code>TYPE</code>
		 */
		public function parse(value:String):* {
			
			var typedValue:* = doParse(value);
			
			if (!(typedValue is type))
				throw new ParserError("ERROR: '" + value + "' cannot be parsed " +
					"into a " + type + " (probably caused by a faulty implemetation " +
					"of doParse())");
					
			return typedValue;
			
		}
		
		
		// PROTECTED METHODS
		
		/**
		 * In this abstract base implementation a copy of the passed value
		 * is returned.
		 * 
		 * @param value <code>String</code> value to test
		 * @return <code>String</code> copy of the value passed
		 * @throws org.molamil.acto.core.errors.ParserError The value can't
		 * 	be converted to the class' <code>TYPE</code>
		 */
		protected function doParse(value:String):* {
			
			// Since value is a String, it returns a copy of value.
			return value;
			
		}
		
		
		// GETTER/SETTER
		
		/**
		 * @return A priority for the order in which the test will be run. 
		 * 	The higher the priority, the earlier the test will be run on a tester that 
		 * 	contains several <code>AbstractTypeParser</code> objects.   
		 */
		public function get priority():uint {
			return _priority;
		}
		
		/**
		 * @param priority A priority for the order in which the test will be run. 
		 * 	The higher the priority, the earlier the test will be run on a tester that 
		 * 	contains several <code>AbstractTypeParser</code> objects.
		 */
		public function set priority(priority:uint):void {
			_priority = priority;
		}
		
		
	}
	
}
